using System;
using System.Collections;
using System.Collections.Generic;
using Gtk;

namespace iPuz.Game
{
	public class GameLoader : IDisposable {
		
		private uint _CorrectAnswersCounter = 0;			// CorrectAnswersCounter to get to an end of this Game.
		public uint CorrectAnswersCounter {
			get { return this._CorrectAnswersCounter; }
		}
		
		//TODO: Generate self Gtk.Table (rows, cols) - add it to main Window
		private Gtk.Table _tblPicsInPlay;
		
		// Create new Game
		public GameLoader(Gtk.Table table)
		{
			_tblPicsInPlay = table;
		}
		
		public void Start(string filename) 
		{
			//TODO: Check if the image is biger than the Table of pics
			int imgW = _tblPicsInPlay.Allocation.Width / (int)_tblPicsInPlay.NRows;
			int imgH = _tblPicsInPlay.Allocation.Height / (int)_tblPicsInPlay.NColumns;
		  	
	    	ImageSplitter isp = new ImageSplitter(filename);						//Start the ImageSplitter
	    	isp.SplitNow ((int)_tblPicsInPlay.NRows, (int)_tblPicsInPlay.NColumns);
	 
	 		List<uint[]> rndList = GetRndList (_tblPicsInPlay.NRows, _tblPicsInPlay.NColumns);	// Get Randome position
	 		
	 		PicBloc picPlay;
	 		int counter = 0; 					 				
 		
 			for (uint y = 0; y < _tblPicsInPlay.NColumns; y++) {
 				for (uint x = 0; x < _tblPicsInPlay.NRows; x++) {
 					uint[] rndBloc = (uint[]) rndList[counter];
 					picPlay = new PicBloc ((System.IO.MemoryStream)isp.streamList[counter],
					                       imgW, imgH,
 											new uint[] {x, x+1, y, y+1}, 
 											new uint[] {rndBloc[0], rndBloc[1], rndBloc[2], rndBloc[3]});
 					
 					// check if picbloc is placed in correct position
					if (picPlay.CheckPosition()) {
						this._CorrectAnswersCounter++;
					} else {
						// Set as Drag Sourc +Events
						Drag.SourceSet (picPlay, Gdk.ModifierType.Button1Mask, sourceTable, Gdk.DragAction.Move);
						picPlay.DragDataGet += OnPicPlay_DragDataGet;
						picPlay.DragBegin += OnPicPlay_DragBegin;
						
						// Set as Drag Target +Events
						Drag.DestSet (picPlay, DestDefaults.All, targetTable, Gdk.DragAction.Move);
						picPlay.DragDataReceived += OnPicPplay_DragDataReceived;
						
						// Set the PicBlocExchange
						picPlay.PicBlocUpdate += OnPicBloc_Updated;
					}
					
					// frame
					Frame frm = new Frame();
					frm.ShadowType = ShadowType.In;
					frm.ModifyBg (StateType.Normal, picPlay.IsLocked ? new Gdk.Color (0, 255, 0) : new Gdk.Color (255, 0, 0));
					
					frm.Add (picPlay);
 					_tblPicsInPlay.Attach (frm, picPlay.CurrentPosition[0], picPlay.CurrentPosition[1],
										picPlay.CurrentPosition[2], picPlay.CurrentPosition[3]);
					
					//TODO: chose to cancel/or not currentPosition	(uint[] pos = picPlay.CorrectPosition;)
	 				counter++;
	 			}
	 		}
	 		
			isp.Dispose(); //--- Clean after the Image Splitter								
	    	_tblPicsInPlay.ShowAll(); //--- Always to show late
		}

		#region "Drag & Drop Work"
		private Gtk.TargetEntry[] sourceTable = new Gtk.TargetEntry[] { new Gtk.TargetEntry ("image/png", 0, 0) };
		private Gtk.TargetEntry[] targetTable = new Gtk.TargetEntry[] { new Gtk.TargetEntry ("image/png", 0, 0) };

		private void OnPicPlay_DragDataGet (object sender, DragDataGetArgs args)
		{				
			Gdk.Atom[] targets = args.Context.Targets;
			args.SelectionData.Set (targets[0], 8, System.Text.Encoding.UTF8.GetBytes ("id"));
		}
		
		private void OnPicPlay_DragBegin (object sender, DragBeginArgs args)
		{
			PicBloc pb = (PicBloc)sender;
			Gtk.Drag.SetIconPixbuf (args.Context, ((Gtk.Image)pb.Child).Pixbuf, 0, 0);
		}
	
		private void OnPicPplay_DragDataReceived (object sender, DragDataReceivedArgs args)
		{
			if (args.Info == 0) {
				PicBloc targetPb = (PicBloc)sender;
				uint[] tmpPosition = new uint[4];
				tmpPosition = targetPb.CorrectPosition;
				Gdk.Pixbuf targetPix = ((Gtk.Image)targetPb.Child).Pixbuf;
				
				PicBloc sourcePb = (PicBloc)Gtk.Drag.GetSourceWidget(args.Context);
				Gdk.Pixbuf sourcePix = ((Gtk.Image)sourcePb.Child).Pixbuf;
				
				// Target PicBloc
				targetPb.Update (sourcePix, sourcePb.CorrectPosition);
				
				// Source PicBloc
				sourcePb.Update (targetPix, tmpPosition);
				
				// notify main gui
				OnGameLoaderAction();
			}
		}
		
		private void OnPicBloc_Updated (object sender)
		{			
			PicBloc pb = (PicBloc)sender;
				
			if (pb.IsLocked) {				
				pb.DragDataGet -= OnPicPlay_DragDataGet;
				pb.DragBegin -= OnPicPlay_DragBegin;
				pb.DragDataReceived -= OnPicPplay_DragDataReceived;
				pb.PicBlocUpdate -= OnPicBloc_Updated;
				
				Gtk.Drag.SourceUnset (pb);
				Gtk.Drag.DestUnset (pb);
				
				_CorrectAnswersCounter++;
			}
		}
		#endregion "Drag & Drop Work"

		/// <summary>
		/// Get Random list of uint[] arrays
		/// </summary>
		/// <param name="NRows">
		/// Number of rows <see cref="System.UInt32"/>
		/// </param>
		/// <param name="NCols">
		/// Number of columns <see cref="System.UInt32"/>
		/// </param>
		/// <returns>
		/// List<uint> of random uint arrays <see cref="ArrayList"/>
		/// </returns>
		private List<uint[]> GetRndList (uint NRows, uint NCols)
		{
			List<uint[]> listBlocs = new List<uint[]>();
			for (uint y = 0; y < NCols; y++) {
 				for (uint x = 0; x < NRows; x++) {
 					listBlocs.Add(new uint[] {x, x+1, y, y+1});
 				}
 			}
		
			System.Random rnd = new Random ();
			List<uint[]> retList = new List<uint[]> ((int)NRows * (int)NCols);
			int ri = 0;
			
			while (true) {
				if (listBlocs.Count == 1) {					// only one (1) item is left
					retList.Add ((uint[])listBlocs[0]);
					break;
				}
				
				ri = rnd.Next(listBlocs.Count);
				uint[] tmpBloc = (uint[])listBlocs[ri];
				retList.Add (tmpBloc);
				
				listBlocs.RemoveAt (ri);
			}
			
			listBlocs = null;
			return retList;
		}
	
		public void Dispose()
		{
			//TODO: foreach PicBloc in tbl
			//	clear pixbuf
			//	clear Image
			//	clear PicBlog + unload events
			//	clear tbl
		}

		public delegate void GameloaderActionEventHandler (object sender, bool gameOver);
		public event GameloaderActionEventHandler GameLoaderAction;
		
		public void OnGameLoaderAction ()
		{
			if (GameLoaderAction != null ) {
				GameLoaderAction (this, CorrectAnswersCounter == 25);
			}
		}
	}
}
